import json
import uuid
from datetime import datetime

from conf.neo4j_cnx import NeoCnxPool

from conf.variable import ES_HOSTS
from elasticsearch import Elasticsearch

from medical_kg.alter_label import AlterLabel
from medical_kg.entity_type import entity_view, entity_model
from web.const import StatusCode

neo_cnx = NeoCnxPool()

es = Elasticsearch(hosts=ES_HOSTS)
alter_label_app = AlterLabel()


class EntityTree:
    def add_rel(self, uid1, uid2, rel_type):
        with neo_cnx.get_session() as session:
            query = (
                        'MATCH (n1:ICD11 {uid:$uid1}) '
                        'MATCH (n2:ICD11 {uid:$uid2}) '
                        'MERGE (n1)-[r:%s]->(n2) '
                        'ON CREATE SET r.uid=$rel_uid'
                    ) % rel_type
            session.run(query, uid1=uid1, uid2=uid2, rel_uid=str(uuid.uuid4()))

    def find_parent_uid(self, uid):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (n1:ICD11 {uid:$uid}) '
                'MATCH (n1)-[r:Parent]->(n2) '
                'RETURN n2.uid AS parent_uid'
            )
            results = session.run(query, uid=uid)
            for record in results:
                return record['parent_uid']

    def query_labels(self, uid):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (n:ICD11 {uid: $uid}) '
                'RETURN labels(n) as label'
            )
            results = session.run(query, uid=uid)
            results = [record['label'] for record in results.data()][0]
            results.remove('ICD11')
            return results

    @staticmethod
    def find_child_uids(uid):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (a:ICD11 {uid: $uid}) '
                'RETURN a.child as child '
            )
            results = session.run(query, uid=uid)
            results = [record['child'] for record in results.data()]
            results = results[0]
            return results

    @staticmethod
    def set_child_uids(uid, child_uids):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (a:ICD11 {uid: $uid}) '
                'SET a.child = $child_uids '
            )
            session.run(query, uid=uid, child_uids=child_uids)

    def create_child_node(self, parent_uid, name, desc, class_kind='category'):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (n1:ICD11 {uid: $parent_uid}) '
                'RETURN n1'
            )
            results = session.run(query, parent_uid=parent_uid)
            for record in results:
                labels_n1 = list(record['n1'].labels)
                labels_n1 = ':'.join(labels_n1)

            query = (
                'MATCH (n1:ICD11 {uid: $parent_uid}) '
                'MATCH (n1)-[:Child]->(n2) '
                'RETURN n2'
            )
            results = session.run(query, parent_uid=parent_uid)
            for record in results:
                n2_glabal_properties = dict(record['n2'].items())
                if name == n2_glabal_properties['name']:
                    return {'status': StatusCode.INFORMATION_REPEATE_QUEST,
                            'message': '', 'payload': {}}

            query = (
                        'MERGE (n:%s {name: $name, '
                        'desc: $desc, '
                        'uid: $child_uid, '
                        'child: $child_list, '
                        'properties: $properties_json}) '
                    ) % labels_n1
            child_uid = str(uuid.uuid4())
            properties_json = json.dumps({
                'name': name,
                'code': '',
                'class_kind': class_kind
            }, ensure_ascii=False)
            session.run(query,
                        name=name,
                        desc=desc,
                        child_uid=child_uid,
                        child_list=[],
                        properties_json=properties_json)

            self.add_rel(parent_uid, child_uid, 'Child')
            self.add_rel(child_uid, parent_uid, 'Parent')

            child_uids = self.find_child_uids(parent_uid)
            child_uids.append(child_uid)

            self.set_child_uids(parent_uid, child_uids)

            doc = {
                'uid': child_uid,
                'index_term': [name],
                'class_kind': class_kind,
                'timestamp': datetime.now(),
            }
            es.index(index="cdss-icd11-index-term", id=child_uid, body=doc)

            return {'status': StatusCode.SUCCESS_CODE,
                    'message': '', 'payload': {'created_uid': child_uid,
                                               'parent_uid': self.find_parent_uid(child_uid)}}

    def delete_node(self, uid):
        if '-' not in uid:
            return {'status': StatusCode.GOD_PERMISSION_DENY,
                    'message': '', 'payload': {}}

        child_uids = self.find_child_uids(uid)
        if len(child_uids) > 0:
            return {'status': StatusCode.DEPENDENCY_ERORR,
                    'message': '', 'payload': {}}

        with neo_cnx.get_session() as session:
            parent_uid = self.find_parent_uid(uid)
            child_uids = self.find_child_uids(parent_uid)
            child_uids.remove(uid)
            self.set_child_uids(parent_uid, child_uids)

            alter_label_app.auto_alter(uid)

            query = (
                'MATCH (n:ICD11 {uid: $uid}) DETACH DELETE n'
            )
            session.run(query, uid=uid)

            es.delete(index="cdss-icd11-index-term", id=uid)

            return {'status': StatusCode.SUCCESS_CODE,
                    'message': '', 'payload': {'parent_uid': parent_uid}}

    def alter_node_info(self, uid, name, desc, labels, class_kind='category'):
        if len(self.query_labels(uid)) == 1:
            alter_label_app.alter_label(uid, entity_model(labels))

        parent_uid = self.find_parent_uid(uid)

        if '-' not in uid:
            with neo_cnx.get_session() as session:
                query = (
                    'MATCH (n:ICD11 {uid: $uid}) '
                    'SET n.desc = $desc'
                )
                session.run(query,
                            uid=uid,
                            desc=desc)
                return {'status': StatusCode.SUCCESS_CODE,
                        'message': '', 'payload': {'parent_uid': parent_uid}}

        else:
            with neo_cnx.get_session() as session:

                query = (
                    'MATCH (n:ICD11 {uid: $uid}) '
                    'SET n.name = $name, '
                    'n.desc = $desc, '
                    'n.class_kind = $class_kind, '
                    'n.properties = $properties_json'
                )

                properties_json = json.dumps({
                    'name': name,
                    'code': '',
                    'class_kind': 'category'
                }, ensure_ascii=False)

                session.run(query,
                            uid=uid,
                            name=name,
                            desc=desc,
                            class_kind=class_kind,
                            properties_json=properties_json)

                doc = {
                    'uid': uid,
                    'index_term': [name],
                    'class_kind': class_kind,
                    'timestamp': datetime.now(),
                }
                es.index(index="cdss-icd11-index-term", id=uid, body=doc)

                return {'status': StatusCode.SUCCESS_CODE,
                        'message': '', 'payload': {'parent_uid': parent_uid}}

    def query_node_info(self, uid):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (n:ICD11 {uid: $uid}) '
                'RETURN n.desc AS desc, n.properties AS properties'
            )
            results = session.run(query, uid=uid)
            for record in results:
                properties_json = json.loads(record['properties'])
                return {'status': StatusCode.SUCCESS_CODE,
                        'message': '',
                        'payload': {
                            'name': properties_json['name'],
                            'desc': record['desc'],
                            'class_kind': properties_json['class_kind'],
                            'labels': entity_view(self.query_labels(uid))
                        }}


if __name__ == '__main__':
    entity_crud_app = EntityTree()

    results = entity_crud_app.query_node_info(uid='2c4d8858-605c-47c2-8325-3e05477364ed')

    entity_crud_app.create_child_node(parent_uid='1435254666',
                                      name='测试TEST',
                                      desc='测试TEST添加')

    entity_crud_app.alter_node_info(uid='2c4d8858-605c-47c2-8325-3e05477364ed',
                                    name='测试TEST2', desc='测试TEST添加', labels='疾病')

    # entity_crud_app.delete_node(uid='f8f278d3-6d6b-4f1e-81e1-9037e248dc3c')
